/*
 * 
 *          ZoneMinder Hardware Project
 *              Laser Sensor
 * 
 * 
 * A sensor communicating
 * via ENC28J60 to ZMTrigger daemon
 * 
 * 
 */


/*
 *        What it does:
 *        
 * 
 */

/*
 * 
 * 
 * 
 *    Directions: 
 *  
 *    Use Arduino Uno
 *    Connect Pin 3 to Reset pin (may not be needed)
 *    Connect ENC28J60 using these instructions:
 *    https://github.com/ntruchsess/arduino_uip
 *    http://web.archive.org/save/https://create.arduino.cc/projecthub/Sourcery/how-to-connect-the-enc28j60-to-an-arduino-efd0dd
 *    CS for ENC and UIP library is 10 by default on UNO. Not 8, like that link says.
 *    
 *    Connect microwave motion sensor such as HB100, or laser diode to A1
 *    Add a speaker for audible debugging
 *    LED can also be added
 *  
 *    
 *    
 *    
 */




#include <UIPEthernet.h>
#include <EEPROM.h>




//Edit the below values

#define DEBUGMODE 1           // 1 == on. 0 == off.


/***************Ethernet ENC28J60***************/

//Mac must be unique
byte mac[]    = { 0xDE, 0xAD, 0xBE, 0xEF, 0xEA, 0x11 };
//IP of Arduino
byte ip[]     = { 192, 168, 1, 177 };
//IP of zm server
byte server[] = { 192, 168, 1, 178 }; 

//ZM server ip to put in requests.
//maybe you can use hostname, not sure.  TODO: test hostnames
String host="192.168.1.178";

//username and password to login to Zoneminder Server.
//If you don't have authentication, you will need to edit the
//script.
//NOTE: not needed for ZMTrigger. Only API.
String username="username";
String password="password";

EthernetClient client;


#define ZMTRIGGERPORT 6802




/***************Pins***************/

#define SPEAKER_PIN 6
#define LED_PIN 9
#define RESETPIN 2   //may not be needed here
#define SENSORPIN A1




/***************Variables************/

int     MotionSensorRead    = 0;
uint8_t AlarmActive         = 0;
char*   ZMTriggerMessage       = "1234567890123456789012345678901234"; //Initialize this with dummy data



// Upper and lower limit for ADC to register motion
// The HB100 outputs a wave that chaotically moves up and down. If the wave reaches a 
// high or low point, we register an alarm.

// These should be tuned depending on your setup and how sensitive you want motion detected
// without getting false alarms. Easiest to test with Serial output

#define UPPERLIMIT            900
#define LOWERLIMIT            100




/*

        ZMTrigger Command to Send

B<id>|B<action>|B<score>|B<cause>|B<text>|B<showtext>
which in this code is:
monnum | onoff + timealarm | score | source

e.g.
2|on+5|100|ZoneAVR||

This will send a command to ZMTrigger.pl to turn monitor #2 ON (alarm state) for five seconds, with a score of 100
and the source of ZoneAVR. The text field, and show text are not setup here.

*/


char* monnum    = "25";                       //monitor number
char* onoff     = "on";                       //command to send to zmtrigger. 
char* timealarm = "10";                       //time to set monitor to alarm
char* score     = "100";                      //score to assign
char* source    = "ZMHW MotionSensor";        //source




//Do not need to edit below







void chime(int freq){
    tone(SPEAKER_PIN, freq, 50);
    delay(50);
}

void chimefast(int freq, int fast){
    tone(SPEAKER_PIN, freq, fast);
    delay(fast);
}

//timer/interrupt
uint16_t timer1;
uint16_t timer1_counter;
uint8_t  debouncetime;
uint8_t  first_interrupt = 0;

//timer for debounce
ISR(TIMER1_OVF_vect){
    timer1++;
    
    if (first_interrupt == 1 ){
      debouncetime++;
    }
    
    if (debouncetime > 2) {
      first_interrupt = 0;
      debouncetime    = 0;
      AlarmActive     = 0;
    }
  
}



void setup()
{

  Serial.begin(9600);
  Serial.println("ZMHW Project");
  Serial.println("Motion Sensor");

  pinMode(SENSORPIN, INPUT);
  pinMode(SPEAKER_PIN, OUTPUT);
  pinMode(RESETPIN, OUTPUT);
  
  
  
  Ethernet.begin(mac, ip);
 
  
  
  //timer 1, setup
  //Crystal of Uno is == Mega (16MHz)


  //this timer is all bodged up, but doesn't matter, as we only
  //need two or three counts between alarms. Not going to fix atm.
  //and it works.
  
  //Clear existing registers
  TCCR1A = 0;
  TCCR1B = 0;

  // Set timer1_counter to the correct value for our interrupt interval
  //timer1_counter  = 10000;    // 62500 for one second if using 256 prescaler. can't be over 16 bit value (timer1 is 16bit limited)
  //timer1_counter  = 10;
  //TCNT1 = timer1_counter;     // TCNT1 is what we are overflowing on
  TCCR1B |= (1 << CS12);    // 256 prescaler  (divide 16mhz/256 = 62500)
  TCCR1B |= 00000101;         // https://web.archive.org/web/20170707164930/http://www.avrbeginners.net:80/architecture/timers/timers.html
                              // search tccr1b
  TIMSK1 |= (1 << TOIE1);     // enable timer overflow interrupt (if goes over timer, interrupt flagged)
  //end timer1



  sei(); //timer needs interrupts, enable interrupts
  
  tone(SPEAKER_PIN, 1000, 200);
  delay(100);
  tone(SPEAKER_PIN, 2000, 200);
  delay(100);
  tone(SPEAKER_PIN, 2200, 200);
  delay(100);
}



void loop()
{


   //The SICK infrared laser requires a transistor to output
   //high or low, but we will cheat and instead, put it through
   //a serial diode, and then use the ADC instead of a digital pin
   //saves a few seconds to put a diode on the end instead of transistor

  
   MotionSensorRead = analogRead(SENSORPIN);
   Serial.print("Motion Sensor Value: ");
   Serial.println(String(MotionSensorRead));
   //Serial.println(String(timer1));
   
   if(DEBUGMODE){
    delay(10);
   }
  

  //upon boot, values are around 400 sometimes, so only alert at higher
    if (MotionSensorRead > 500 && AlarmActive == 0){
    Serial.println("Motion Detected");

    //firstpacketsend    = 0;

    cli();
    //some of this may be redundant, need to check
    AlarmActive        = 1;
    first_interrupt    = 1;
    debouncetime       = 0;
    sei();

    //Want the chime to be only noticeable if you know what to listen
    //for. Make it a high freq. sound that is easy to miss.
    chime(13000);

         
    Serial.println("Connecting...");


    if (client.connect(server, ZMTRIGGERPORT)) {

      chime(13000);


    
      //beware that the buffer in snprintf is big enough to hold everything
      snprintf(ZMTriggerMessage, 56, "%s|%s+%s|%s|%s||", monnum, onoff, timealarm, score, source);
      Serial.print("the TCP Packet being sent:");
      Serial.println(String(ZMTriggerMessage));
    
      
      client.println(String(ZMTriggerMessage)); //required


      Serial.println("TCP packet sent to ZMTrigger");
      client.stop();
    } 
    else {
      //NOTE: If you are not connected to the network
      //the device will currently freeze up, and not timeout.
      //Need to implement a watchdog.
      //If you ARE connected to the network, and server is not available
      //then it will timeout.
      
      Serial.println("Connection to ZM Server failed");

      chime(50);
      delay(100);
      chime(50);
      delay(100);
      chime(50);
      delay(100);
    }
  }



     
    //disconnect
    if (!client.connected()) {
      client.stop();
      
    }








        
          //write to eeprom if connection failed, and try again upon reboot
          //EEPROM.write(EEPROM_RETRY, switch_pressed);
      
      



    
} //end main loop
